-[ 0x0C ]--------------------------------------------------------------------
-[ Unete al Software libre ]-------------------------------------------------
-[ by FCA00000 ]-----------------------------------------------------SET-34--


Este artculo describe los pasos que he seguido para mejorar un programa de 
cdigo libre. El nivel es bajo, puesto que el objetivo es demostrar que es 
fcil subirse al carro del software libre; no slo como pasajero, sino como 
tripulacin.

Este texto quizs es muy obvio para algunos; pero espero que tambin sirva para
aquellos que quieren un empujoncito para meterse en el mundo de la programacin.

Sin ms prembulo, empezar recordando mi situacin hace algunos aos: 
deambulaba yo por la sala de ordenadores de la universidad, y vi que un tipo 
perda el tiempo con un juego de mover fichas por un mapa. En esa poca estaba 
yo aficionado a los juegos de tablero (risk, Squad Leader, ...) as que 
despert mi inters.
Al da siguiente me acerqu a la sala de nuevo y el chico estaba jugando de 
nuevo. Lo mismo sucedi al otro da, y me d cuenta de que el juego pareca 
ser bastante adictivo. Eso, o el individuo era un ludpata.
Me acerqu para preguntarle, y me dijo que el juego se llamaba ... Civilization:
uno de los mejores juegos de estrategia de la historia.
Lo copi y lo instal en casa. Al principio pareca complicado porque haba 
muchos tipos de unidades, pero la verdad es que requera esfuerzo dejar de 
jugar. Desde ese momento, yo calculo que he desperdiciado unas 2.000 horas a lo 
largo de 5 aos.
Luego publicaron la tpica secuela llamada Colonization, pero no me atrajo lo 
suficiente.

El tiempo ha pasado, ahora me gano el sueldo haciendo programas, incluidos un 
par de lenguajes que casi nadie conoce, y solo de vez en cuando vuelvo a echar 
una partida.

Con el auge de Internet, era inevitable que surgieran versiones de Civilization,
la ms avanzada se llama FreeCiv, y es bastante fiel a la original, excepto que
no me enganch.
Y tambin ha salido una adaptacin de Colonization, llamada FreeCol (estos 
tipos no tienen imaginacin para los nombres).
Ya que no me atrap en sus inicios, decid darle una segunda oportunidad a esta
nueva versin.

La bajo, la instalo, leo la documentacin inicial, y me pongo a jugar. El juego
en s es fcil de aprender, y lamentablemente los enemigos no son lo bastante 
inteligentes como para ponerme en aprietos. Si aumentas el nivel de dificultad,
lo nico que pasa es que es ms gravoso conseguir los recursos y el avance es 
ms lento.
Lo que me sorprendi es que el juego parece bastante maduro. No se cuelga, la 
parte grfica es aceptablemente rpida, y no tarda una eternidad en mover las 
fichas enemigas.

Al ser un programa de software libre, el cdigo fuente est disponible, as que 
lo baj por curiosidad. Y la primera sorpresa es que est escrito en Java !
Eso explica porqu tarda 20 segundos en arrancar, y usa 100 Mg de memoria. 
Tambin destroza mi creencia de que Java es lento.

Una primera ojeada muestra que est dividido en mdulos:
-el interface grfico
-el modelo de control de unidades, colonias, y terreno
-el servidor, usado en modo multijugador
-la inteligencia artificial

En total, unas 300 clases, aunque slo la mitad son interesantes: el resto son 
interfaces, pequeas variaciones (override) de otras clases, y clases con 
constantes y excepciones.

Despus de jugar un par de partidas en 10 horas, me hago una idea de los 
conceptos del juego. Entiendo que hay:
-jugadores
	-colonias
		-unidades
		-edificios
		-productos
		-consumos (impuestos, comida, cruces)
	-padres fundadores
-mapa
	-terreno
-turnos

Por tanto, espero encontrar clases de estos tipos. Pero no adelantemos 
acontecimientos.

El programa ejecutable viene dentro del fichero FreeCol.jar que ocupa 3 Mb en 
el disco, y se puede iniciar con
java -Xmx128M -jar FreeCol.jar

El siguiente paso es obtener el cdigo fuente freecol-0.6.1-src.tar.gz y 
descomprimirlo en algn sitio.

Ahora necesito un programa para navegar por los fuentes, y compilar los cambios.
Yo hace bastantes aos que no programo en Java, as que no estoy muy puesto al 
da de los compiladores, entornos de desarrollo, y libreras necesarias. 
Visitando las webs habituales, decido instalar
java 1.6.0 como runtime
NetBeans 5.5 como IDE (Integrated Development Enviroment)

As, de paso, aprendo este entorno. No digo que sea el mejor, sino que es el 
primero que apareci en mi bsqueda.

Genero un nuevo proyecto, tomando como base el directorio donde he 
descomprimido las fuentes.

Empiezo a navegar por el cdigo fuente y me familiarizo con el sistema de 
manejar ficheros.
Para ello uso la ventana "Archivos" que los muestra segn la estructura de 
directorios.
Otra cosa que necesito es poder seleccionar una clase, y ver su cdigo. Esto se 
hace con el botn derecho, y:
Go To -> Source
Go To -> Declaration

Tambin es til el men Find Usages, y el Edit->FindInProjects

Obviamente tengo que ser capaz de re-compilar mis cambios. Esto se consigue con 
el men Build, o tambin con Run. Incluso es mejor el Debug, que permite poner 
breakpoints.

Intento la primera compilacin, y se queja de no encontrar la clase en la lnea
import cz.autel.dmi.HIGLayout;

Esta no me suena que sea una clase normal de java, ni tampoco parece ser 
perteneciente a este proyecto.
Busco en el disco una librera cz.* pero no encuentro nada.
Despus busco  HIGLayout.*
y encuentro
freecol/jars/higlayout.jar

La descomprimo y veo que efectivamente contiene dicha clase.
Ahora hay que decirle a NetBeans que use el directorio freecol/jars , lo cual 
se hace en
File->Project->Properties->Libraries->Compile-time 

Lo intento de nuevo, y ahora compila sin problemas.
Magnfico: realmente el cdigo fuente est preparado para los que quieren 
modificarlo.

El siguiente paso es ejecutar el programa. Tambin se queja de que no encuentra 
las libreras, pero se arregla de manera similar, en el sub-men 
Libraries->Run-time 

Eso s: el entorno usa 100 Mg de memoria, y el juego otros 100 Mg. El 
Civilization original funcionaba en MS-DOS con 500 Kb. de memoria !

Vamos a probar a hacer algn cambio.
Lo ms visible del juego es obviamente la parte grfica. Este es el punto de 
inicio para ir tirando del ovillo.
Por ejemplo, el informe del consejero de asuntos externos (Report->Foreign 
Affairs Advisor) nos muestra la actitud diplomtica que tenemos con otros 
jugadores, en una lista que incluye la frase:
Stance:         Peace

Para saber dnde se define dicha variable, buscamos la palabra "Stance" y la 
encontramos en el fichero
FreeColMessages.properties
en la lnea
report.stance=Stance

esto hace extremadamente fcil la tarea de los traductores, pues las frases que 
se muestran al usuario estn almacenadas en un fichero, que se puede traducir 
sin necesidad de recompilar el programa.

Ahora busco la palabra "report.stance" y la encuentro, entre otros, en el 
fichero
ReportForeignAffairPanel.java
en la lnea
enemyPanel.add(new JLabel(Messages.message("report.stance")), higConst.rc(row, 
labelColumn));
int stance = Player.getStance();
enemyPanel.add(new JLabel(Player.getStanceAsString(stance)), higConst.rc(row, 
valueColumn));

La primera lnea lo que hace es aadir una etiqueta con el texto obtenido de
Messages.message("report.stance")

Est claro que esta clase se encarga de leer el texto adecuado en funcin del 
idioma del usuario.
Por ejemplo, el fichero
FreeColMessages_it_IT.properties
contiene la traduccin a italiano, que contiene
report.stance=Linea Diplom.

Volviendo al programa, la siguiente lnea
int stance = Player.getStance();

nos lleva (GoTo Source) hasta la clase Player.java y hace
public int getStance(Player player) {
        return getStance(player.getNation());
    }

O sea, que es una funcin de salto a:
public int getStance(int nation) {
        if (nation == NO_NATION) {
            return 0;
        } else {
            return stance[nation];
        }
    }

Que a su vez nos referencia al array
stance[];

que est definido (GoTo Declaration) al principio de esta clase, como
private int[] stance = new int[NUMBER_OF_NATIONS];

y se usa (Find Usages) en
public void setStance(Player player, int newStance)

Ah encontramos que los posibles valores son:
public static final int WAR = -2, CEASE_FIRE = -1, PEACE = 0, ALLIANCE = 1;

A su vez, setStance se invoca desde las funciones:
Player.declareIndependence	-> WAR contra la madre patria
Player.giveIndependence	->PEACE con la madre patria
Monarch.declareWar	-> WAR contra otro jugador
AIPlayer.determineStances	-> un jugador no-humano tiene ganas de guerra

ahora supongamos que queremos que el juego sea pacfico, y que no queremos 
los con los jugadores robots. Entonces antes de
stance[player.getNation()] = newStance;

incluimos
if(newStance==WAR && player.isAI())   newStance=CEASE_FIRE;

Obviamente no todos los jugadores estarn de acuerdo con esta regla, as que se 
podra incluir como una opcin.
Tambin se podra alterar en funcin de:
-el ao: al principo del juego sera calmado, y el final sera ms violento
-la nacionalidad: los holandeses son ms favorables a la paz; los espaoles ms
 belicosos
-el poder militar: no es bueno enfadar a una nacin poderosa
-la riqueza: la tentacin de un buen tesoro es difcil de resistir
-los padres fundadores: Benjamn Franklin es sosegado; Hernn Corts irascible

Pero en vez de alterar el juego, vamos a mejorarlo.
En esta rutina setStance hay un error: si declaras la guerra a otro jugador, lo opuesto no sucede.
Esto es malo porque el estado de guerra proporciona algunas ventajas en la lucha, y no parece justo que el agraviado ofrezca la otra mejilla.

Notar que la funcin (pseudo-cdigo) es
Atacante.setStance(Victima, WAR);

por lo que es necesario incluir una nueva lnea
Victima.setStance(Atacante, WAR);

lo cual se hace antes del final:
if(newStance==WAR && player.getStance(this) !=WAR)	player.setStance(this,WAR);

puesto que
this         es Atacante
player      es Victima

Es decir, intercambiamos los papeles.

Una vez que hemos solucionado el bug y lo hemos probado, lo normal es 
publicarlo.
Dependiendo del tipo de control que los autores del programa quieren tener, 
ser necesario subcribirse a algn tipo de repositorio colaborativo, 
normalmente en un sitio controlado por CVS o SVN, tal como sourceforge.
En ese caso, el sitio es http://www.freecol.org
que apunta a
freecol.sourceforge.net
y la lista de correo es
freecol-users@lists.sourceforge.net

Al principio es una buena net-etiqueta el presentarse, y ofrecer tu cooperacin.
Los cambios pequeos, y los que haces en los primeros dias tras unirte al grupo, 
se pueden mandar en un correo diciendo algo as como:

Estimado seor programador,

en el fichero
src/net/sf/freecol/common/model/Player.java
cerca de la lnea 2073
dice
...
if (oldStance == PEACE && newStance == WAR) {
...

y yo creo que se podra mejorar, provocando la guerra complementaria.
Esto es, debera decir
...
if(newStance==WAR && player.getStance(this) !=WAR)	
  player.setStance(this,WAR);
if (oldStance == PEACE && newStance == WAR) {
...

Atentamente,
otro programador.

En este caso particular, una revisin de la lgica del juego demostr que este 
error no era un error: es intencionado. Esto demuestra que todos cometemos 
errores, sobre todos los novatos. Antes de abrir la boca, hay que saber de lo 
que se habla.



Al cabo del tiempo, cuando los otros programadores confan en t, puedes crear 
un usuario en sourceforge y trabajar directamente sobre el repositorio.
Para ello tienes que instalar CVS/SVN y luego:
-transferir la versin ms reciente a tu ordenador
-averiguar qu fichero quieres modificar
-checkout
-hacer los cambios
-probarlos
-checkin

En algunos grupos de colaboracin slo el administrador puede hacer cambios. En 
este caso debes mandarle los cambios para que los pueda revisar e instalar. 
Esto se hace en forma de parches:
Primero debes tener la versin ms reciente del cdigo fuente.
Luego haces los cambios en tu ordenador, probando que todo funciona bien.
Comparas los ficheros:
diff -U 3 -H -d -p -r -N original/src/..../Player.java FCA00000/src/..
                                                                ../Player.java

que genera un fichero parecido a esto:

diff -U 3 -H -d -p -r -N original/src/net/sf/freecol/common/model/Player.java 
                         FCA00000/src/net/sf/freecol/common/model/Player.java
--- original/src/net/sf/freecol/common/model/Player.java 	
                                             2007-04-03 19:48:42.000000000 +0200
+++ FCA00000/src/net/sf/freecol/common/model/Player.java 	
                                             2007-05-07 15:19:58.000000000 +0200
@@ -2073,33 +2073,34 @@ public void setStance(Player player, int newStance) {
+if(newStance==WAR && player.getStance(this) !=WAR)	
                                                     player.setStance(this,WAR);
if (oldStance == PEACE && newStance == WAR) {

como puedes ver, incluye el nombre del fichero modificado, la funcin donde 
est en cambio, la lnea original, el cdigo original, y el cdigo aadido.

Esto se guarda en un fichero
FCA00000.2007.05.07.diff
y se le manda al administrador del proyecto, que lo incluir si le parece bien.

Si tu nuevo cdigo es til y est bien escrito, lo incluirn. Tambin es 
posible que lo cambien para adecuarse a las normas de escritura (maysculas, 
indentado, formateado, ...) y no debes ofenderte si recibes alguna crtica. Al 
fin y al cabo, ellos estaban antes, as que eres t el que debe adecuarse a sus 
reglas.

Siguiendo con las modificaciones, voy a dar otro ejemplo de un cambio que yo he 
hecho a este programa.
El objetivo del juego es declarar la independencia de la madre patria. Para eso 
fundas colonias y construyes edificios. Una vez que has completado uno puedes 
empezar con otro, pero solo sucede automticamente en unos pocos casos: si 
acabas de construir un almacn, inmediatamente empiezas a trabajar en uno ms 
grande. Cuando lo finalizas, no hay otra ampliacin disponible.
Esto provoca que algunas de tus colonias no construyen nada, lo cual es una 
prdida de recursos.

Es posible hacer aparecer un men con la lista de todas tus colonias, y te 
muestra los edificios ya construidos, adems del que se est construyendo (en 
color gris). Lamentablemente no es evidente que no se est construyendo nada.
Dnde se programa esta lista?
Bueno, el ttulo es "Colony Advisor" que est definido como
menuBar.report.colony=Colony Advisor

que, entre otros, aparece en
ReportProductionPanel.java

que hace
...
add(new JLabel(Messages.message("Colony")), higConst.rc(1, colonyColumn));
...
for (int colonyIndex = 0; colonyIndex < colonies.size(); colonyIndex++) {
        ...
        Building building = colony.getBuildingForProducing(goodsType);
        }

As que pongo un breakpoint en
getBuildingForProducing
y arranco el programa.
Hago aparecer el panel "Colony Advisor" y efectivamente acaba en el breakpoint. Lo que me sorprende es que mirando el stack (pila de llamadas) descubro que viene desde
ReportColonyPanel.java

Bueno, me he equivocado de panel, pero he llegado al mismo punto.
Analizo la rutina
private JPanel createBuildingPanel(Colony colony) {
...
for (int buildingType = 0; buildingType < Building.NUMBER_OF_TYPES; 
                                                               buildingType++) {
        buildingPanel.add(new JLabel(building.getName()));
        if (buildingType == colony.getCurrentlyBuilding()) {
                buildingLabel.setForeground(Color.GRAY);
                }
        }

Lo cual quiere decir:
-para esta colonia:
	-para cada edificio:
		-mustralo
		-si se est construyendo, ponlo de color gris

Lo que quiero hacer es que si no se est construyendo nada, mostrar una lnea 
en rojo que lo diga.
Algo as como:
if(colony.getCurrentlyBuilding()==NULL)
	buildingPanel.add(new JLabel("No se est construyendo nada"),
                                                                    Colour.RED);
	

Hay varios pequeos detalles:
-primero, getCurrentlyBuilding() devuelve -1 , no NULL
-segundo, que JLabel no admite un color como segundo argumento.
-por ltimo, que queremos que aparezca en todos los idiomas

Todos estos ajustes son detectados por el compilador, por lo que no es 
necesario probar el programa para darse cuenta de que no funciona.

De hecho, la manera correcta es
        if (colony.getCurrentlyBuilding() == -1) {
            JLabel unitLabel = new JLabel(Messages.message("nothing"));
            unitLabel.setForeground(Color.RED);
            buildingPanel.add(unitLabel);
        }


Es ms, el entorno NetBeans permite modificar el cdigo en tiempo de ejecucin. 
Usando el men Run->ApplyCodeChanges los cambios quedan reflejados 
inmedatamente. Por supuesto que no todas las clases permiten esto, pero si 
funciona, es un esfuerzo que te ahorras.

Probarlo es fcil: hago que una colonia no construya nada, produzco el informe, 
y efectivamente se muestra una lnea "Nothing" en color rojo chilln.

En los primeros dias de jueguetear con el cdigo fuente es habitual estar 
perdido y navegar desde una clase a otra sin encontrar lo que buscas, Aparte de 
poner breakpoints y examinar el stack, tambin es til tracear el programa.

En este caso, los autores han previsto que necesitan saber lo que va haciendo 
el programa. No slo para ellos mismos, sino para que otro usuario que tenga un 
error pueda mandar un informe detallado de la situacin.
Eso se consigue a travs de una clase
logger
que escribe en un fichero dependiendo del nivel de detalle que necesitas. 
Incluye mtodos
severe, warning, info, config, fine, finer, finest

Cuando quiero saber cules han sido las rutinas y clases ejecutadas ms 
recientemente, slo tengo que mirar las ltimas lneas de este fichero.
Esto es un mtodo realmente cmodo. Es sorprendente que haya muchos proyectos 
que no incluyen una traza de ejecucin, sobre todo cuando disponen de capacidad 
de hacerlo. Me pregunto cmo hacen para debuggear errores en el entorno de 
produccin.


Tambin he encontrado til el uso del parmetro
java -verbose
cuando se ejecuta el programa. Esto muestra todas las clases que se van 
cargando, por lo que resulta ms eficiente para saber dnde buscar.

Bueno, eso es todo. Ahora, ve y busca un proyecto en sorceforge. Seguro que 
encuentras alguno en el que cooperar.

Yo, voy a ver si acabo con los Franceses de Louis XIV en Martinique.

Este artculo ha sido escrito en 5 horas, sin contar la investigacin inicial 
sobre los fuentes de Colonization.
Durante su redaccin, ningn vegetal fue sometido a daos innecesarios.

*EOF*